package pildemil;

import java.util.Arrays;

class AB {

    int[] A;	// vecteur d'état
    int[][] B;	// matrice de transaction

    /* 
     * Constructeur
     * initialise A avec paramètre Adep
     * initialise B à 0
     */
    public AB(int[] Adep) {
        int taille = Adep.length;
        
        this.A = new int[taille];
        System.arraycopy(Adep, 0, this.A, 0, taille);

        this.B = new int[taille][taille];
        for(int i=0; i<taille; i++) {
            for(int j=0; j<taille; j++) {
                B[i][j] = 0;
            }
        }
    }

    /* 
     * Constructeur par copie
     * copie ABdep dans AB
     */
    public AB(AB ABdep) {
        int taille = ABdep.A.length;

        this.A = new int[taille];
        System.arraycopy(ABdep.A, 0, this.A, 0, taille);

        this.B = new int[taille][taille];
        for(int i=0; i<taille; i++) {
            System.arraycopy(ABdep.B[i], 0, this.B[i], 0, taille);
        }
    }

    /* 
     * Affiche A et B
     */
    public void afficher() {
        int taille = this.A.length;

        System.out.print("A: ");
        for (int i = 0; i < taille; i++) {
            System.out.print(this.A[i] + " ");
        }
        System.out.print("\n");

        System.out.println("B: ");
        for (int i = 0; i < taille; i++) {
            for (int j = 0; j < taille; j++) {
                System.out.print(this.B[i][j] + " ");
            }
            System.out.print("\n");
        }
        System.out.print("\n");
    }

    /* 
     * Affiche la suite de transactions
     */
    public void verifier() {
        int taille = this.A.length;

        System.out.print("A: ");
        for (int i = 0; i < taille; i++) {
            System.out.print(this.A[i] + " ");
        }
        System.out.print("\n");

        for (int i = 0; i < taille; i++) {
            for (int j = 0; j < taille; j++) {
            	if(B[i][j] != 0) {
            		System.out.println("transaction = " + this.B[i][j]);
            		
            		this.A[i] -= this.B[i][j];
            		this.A[j] += this.B[i][j];

                    System.out.print("A: ");
                    for (int x = 0; x < taille; x++) {
                        System.out.print(this.A[x] + " ");
                    }
                    System.out.print("\n");
            	}
            }
        }
    }

    /*
     * Trasnsaction de i vers j
     * ajoute la transaction à Bij
     * ajoute la valeur de Ai à Aj
     * positionne Ai à 0
     */
    public void transaction(int i, int j) {
        this.B[i][j] += this.A[i];
        this.A[j] += this.A[i];
        this.A[i] = 0;
    }

    /*
     * Retourne TRUE si A est null, FALSE sinon
     */
    public boolean nulA() {
        boolean retour = true;
        int taille = this.A.length;
        
        for (int x = 0; x < taille; x++) {
            if (this.A[x] != 0)
                retour = false;
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est null, FALSE sinon
     */
    public boolean nulA(int i) {
        boolean retour = false;
        if (this.A[i] == 0) {
            retour = true;
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est positif, FALSE sinon
     */
    public boolean posA(int i) {
        boolean retour = false;
        if (this.A[i] > 0) {
            retour = true;
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est négatif, FALSE sinon
     */
    public boolean negA(int i) {
        boolean retour = false;
        if (this.A[i] < 0) {
            retour = true;
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est le max, FALSE sinon
     */
    public boolean maxA(int i) {
        boolean retour = true;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
        	if(this.A[i] < this.A[x]) {
        		retour = false;
        	}
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est le min, FALSE sinon
     */
    public boolean minA(int i) {
        boolean retour = true;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
        	if(this.A[i] > this.A[x]) {
        		retour = false;
        	}
        }
        return retour;
    }
            

    /*
     * Retourne TRUE si Ai est le max absolu, FALSE sinon
     */
    public boolean maxAbsA(int i) {
        boolean retour = true;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
        	if(Math.abs(this.A[i]) < Math.abs(this.A[x])) {
        		retour = false;
        	}
        }
        return retour;
    }

    /*
     * Retourne TRUE si Ai est le min absolu et != 0, FALSE sinon
     */
    public boolean minAbsA(int i) {
        boolean retour = true;
        int taille = this.A.length;

        if(this.A[i] == 0) {
        	retour = false;
        }
        
        for (int x = 0; x < taille; x++) {
        	if((this.A[x] !=0) && (Math.abs(this.A[i]) > Math.abs(this.A[x]))) {
        		retour = false;
        	}
        }
        return retour;
    }
    
    public boolean minDiffA(int i, int j) {
        boolean retour = true;
        int taille = this.A.length;
        int[][] diff = new int[taille][taille];
        
        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
            	diff[x][y] = Math.abs(this.A[x] + this.A[y]);
        	}
        }
        
        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
            	if ((this.A[x] != this.A[y]) && (this.A[x] != 0) && (this.A[y] != 0) && (diff[i][j] > diff[x][y])) {
            		retour = false;
            	}
        	}
        }
        return retour;
    }

    /*
     * Compte les transactions dans B
     * transaction : Bij != 0
     */
    public int nbTrans() {
        int somme = 0;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
                if (this.B[x][y] != 0) {
                    somme++;
                }
            }
        }
        return somme;
    }

    /*
     * somme des valeurs absolues des transactions (B)
     */
    public int somB() {
        int somme = 0;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
                somme += Math.abs(this.B[x][y]);
            }
        }
        return somme;
    }
    
    /*
     * somme des valeurs absolues des états (A)
     */
    public int somA() {
        int somme = 0;
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
            somme += Math.abs(this.A[x]);
        }
        return somme;
    }

    /*
     * rend la matrice de transactions (B) positive
     * transaction Bij équivalent à -Bji
     */
    public void reduire() {
        int taille = this.A.length;

        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
                if(this.B[x][y] < 0) {
                    this.B[y][x] -= this.B[x][y];
                    this.B[x][y] = 0;
                }
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) return true;
        if ( !(o instanceof AB) ) return false;
        AB ab = (AB)o;
        
        int taille = this.A.length;
        for (int x = 0; x < taille; x++) {
            for (int y = 0; y < taille; y++) {
                if(this.B[x][y] != ab.B[x][y]) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 47 * hash + Arrays.hashCode(this.A);
        hash = 47 * hash + Arrays.deepHashCode(this.B);
        return hash;
    }
}
